package agentFramework.agent;

import java.util.ArrayList;
import java.util.Deque;
import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import agentFramework.action.Action;
import agentFramework.core.Message;
import agentFramework.core.MessageDispatcher;
import agentFramework.core.env.Environment;
import agentFramework.strategy.Strategy;


public abstract class Agent extends Thread
{
	private int						id;					// the agent's id
	private MessageDispatcher		messageDispatcher;  // the object that delivers the messages
	private Environment				environment;        // the reference to the environment
	
	public  ArrayList<Integer>		neighborList;  // the list of neighbors ids
	
	public 	Deque<Message>			messageQueue;  // the message queue
	public  Lock					lock;          // a lock object using in accessing the message queue
	
	public  Strategy				agentStrategy; // the object that encapsulates the agent's game strategy
	public  Action					currentAction; // the best considered action till the current moment
	
	public int						coreId;
	public String					name;
	
	private double 					earnings;

	
	// constructor
	public Agent(int id, int coreId, Environment env)
	{
		this.id = id;
		this.neighborList = null;
		this.messageQueue = new LinkedList<Message>();
		this.environment = env;
		this.messageDispatcher = env.getMessageDispatcher();
		this.lock = new ReentrantLock();
		this.agentStrategy = null;
		this.currentAction = null;
		this.coreId = coreId;
		this.name = "Agent_" + id;
		this.earnings = 0.0;
	}
	
	
	// return the current earnings
	public double getEarnings()
	{
		return this.earnings;
	}
	
	
	// update the earnings
	public void addEarnings(double newEarnings)
	{
		this.earnings += newEarnings;
	}
	
	
	// thread's main loop
	public void run()
	{
		this.neighborList = environment.getNeighbors(this);
		this.agentStrategy.init();
		
		behavior();
	}
	

	public void setStrategy (Strategy s)
	{
		this.agentStrategy = s;
	}
	
	
	// adds a message in the agent's message queue
	// this is practically used only by the MessageDispatcher object
	public final void addMessage(Message msg)
	{
		lock.lock();
		
		if (msg.isHighPriority())
		{
			messageQueue.addFirst(msg);
		}
		else
		{
			messageQueue.addLast(msg);
		}
		
		lock.unlock();
	}
	
	
	// this function passes the message to the MessageDispatcher object
	// to deliver it using the message header info (type, destination etc.)
	public final void sendMesage(Message msg)
	{
		messageDispatcher.addMessage(msg);
	}
	
	
	// returns the agent's id
	public final int getAgentId()
	{
		return this.id;
	}
	
	
	// returns the current agent's action
	public final Action getCurrentAction()
	{
		return this.currentAction;
	}
	

	// checks if the environment is still active
	public final boolean isEnvironmentActive()
	{
		return this.environment.getCore().isAlive();
	}
	
	// TO BE IMPLEMENTED
	// this function handles the given message taking a proper action 
	public abstract void handleMessage(Message msg);
	
	
	// TO BE IMPLEMENTED 
	// this function describes the way the agent works, how and when 
	// the messages are processed etc. 
	public abstract void behavior();
}
